#include "config.h"

#include "lsv_wbuffer_internal.h"

/**
 * 1 init hash table for manage wbuffer
 * */
int lsv_wb_hash_index_init(lsv_volume_proto_t *lsv_info){
        int ret = 0;
        index_block_head *wb_hash_index = NULL;

        wb_hash_index = (index_block_head *) malloc(sizeof(index_block_head) * WBUF_INDEX_SIZE);
        if(NULL == wb_hash_index){
                DERROR("malloc hash index failed.\n");
                ret = ENOMEM;
                GOTO(err_ret, ret);
        }
        memset(wb_hash_index, 0, sizeof(index_block_head) * WBUF_INDEX_SIZE);

        for(int i = 0; i < WBUF_INDEX_SIZE; i++){
                INIT_LIST_HEAD(&wb_hash_index[i].list);
        }

        lsv_wbuf_t *wbuf = lsv_info->wbuf;
        wbuf->wb_hash_index = wb_hash_index;
        lsv_rwlock_init(&wbuf->wb_hash_rwlock);

        return 0;
err_ret:
        if (ret > 0) {
                ret = -ret;
        }
        return ret;
}

int lsv_wb_hash_index_destroy(lsv_volume_proto_t *lsv_info){
        lsv_wbuf_t * wbuf = lsv_info->wbuf;

        if (wbuf->wb_hash_index != NULL) {
                yfree((void **)&wbuf->wb_hash_index);
        }

        return 0;
}

int lsv_wb_hash_index_insert(lsv_volume_proto_t *lsv_info, lsv_wbuf_segment_t *segment,
                lsv_u64_t lba, lsv_u32_t chunk_id, lsv_u32_t chunk_page_idx){
        int ret = 0;
        lsv_wbuf_t * wbuf = NULL;
        index_block_head * wb_hash_index = NULL;
        index_block *tmp_index_block = NULL;
        index_block *hash_block_array = NULL;
        lsv_s32_t hash_key = HASH_KEY(lba);

        YASSERT(lba % LSV_PAGE_SIZE == 0);

        hash_block_array = segment->hash_block_array;
        tmp_index_block = &hash_block_array[chunk_id * LSV_WBUF_PAGE_NUM + chunk_page_idx];

        tmp_index_block->offset = lba;
        tmp_index_block->segment = segment;
        tmp_index_block->chunk_id = chunk_id;
        tmp_index_block->chunk_off = chunk_page_idx * LSV_WBUF_PAGE_SIZE;

        if (tmp_index_block->hook.next != NULL) {
                YASSERT(tmp_index_block->hook.next == &tmp_index_block->hook);
        }

        if (tmp_index_block->hook.prev != NULL) {
                YASSERT(tmp_index_block->hook.prev == &tmp_index_block->hook);
        }

        // 插入头部，保证最新的出现在头部；查找时，返回第一个匹配的(最新）
        wbuf = lsv_info->wbuf;
        wb_hash_index = wbuf->wb_hash_index;
        list_add(&tmp_index_block->hook, &wb_hash_index[hash_key].list);

        return ret;
}

int lsv_wb_hash_index_gc(lsv_wbuf_segment_t *segment){
        int  ret = 0;
        lsv_s32_t i = 0, j = 0;
        lsv_chunk_buf_t *chunk_buf = NULL;
        index_block * tmp_index_block = NULL;

        for(i = 0; i < LSV_WBUF_CHUNK_NUM; i++){
                chunk_buf = &(segment->lsv_chunk[i]);

                // 依次从全局hash里，删除所有记录
                for(j = 0; j < chunk_buf->page_idx; j++) {
                        tmp_index_block = &segment->hash_block_array[i * LSV_WBUF_PAGE_NUM + j];
                        list_del_init(&tmp_index_block->hook);
                }

                chunk_buf->page_idx = 0;
        }
        return ret;
}

int lsv_wb_hash_index_lookup(index_block_head *hash, lsv_u64_t offset, lsv_u32_t size, buffer_t *append_buf) {
        lsv_s32_t hash_key = 0;
        lsv_u64_t off_start = 0;
        lsv_s32_t page_offset = 0;
        lsv_s32_t chunk_id = 0;
        lsv_u32_t chunk_off = 0;
        index_block * tmp_index_block = NULL;
        // index_block_head * wb_hash_index = NULL;
        lsv_chunk_buf_t *chunk_buf = NULL;
        struct list_head *pos;
        lsv_wbuf_segment_t * segment = NULL;

        page_offset = offset % LSV_WBUF_PAGE_SIZE;
        off_start = offset - page_offset;

        YASSERT(off_start % LSV_PAGE_SIZE == 0);

        /*process the offset of page start*/
        hash_key = HASH_KEY(off_start);

        // DINFO("hash %u list size %d\n", hash_key, list_size(&hash[hash_key].list));

        list_for_each(pos, &hash[hash_key].list){
                DINFO("pos %p\n", pos);
                tmp_index_block = (index_block *)pos;
                if(tmp_index_block->offset == off_start){
                        segment = tmp_index_block->segment;
                        chunk_id = tmp_index_block->chunk_id;
                        chunk_off = tmp_index_block->chunk_off;
                        chunk_buf = &segment->lsv_chunk[chunk_id];

                        mbuffer_appendmem(append_buf,
                                          chunk_buf->page_buf + chunk_off + page_offset,
                                          size);
                        DINFO("off_start %lld, hash_key %d, segment %p, chunk_idx %d, chunk_offset %d\n",
                              (LLU)off_start,
                              hash_key,
                              segment,
                              tmp_index_block->chunk_id,
                              tmp_index_block->chunk_off);
                        return size;
                }
        }
        return 0;
}
